package org.sshbot.transport;

import android.util.Log;
import android.widget.Toast;

import com.jcraft.jsch.*;

import de.mud.telnet.TelnetProtocolHandler;
import de.mud.terminal.VT320;
import ext.regex2.Pattern;

import org.sshbot.util.LogHelper;

import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.net.InetAddress;
import java.net.Socket;
import java.net.UnknownHostException;
import java.util.Properties;

import static org.sshbot.util.LogHelper.DEBUG;
import static org.sshbot.util.LogHelper.ERROR;
import static org.sshbot.util.LogHelper.INFO;

/**
 * User: parkerkane
 * Date: 13.12.2010
 * Time: 17:14
 */
public class TELNET extends Transport {

	private static final String TAG = "ConnectBot.Telnet";
	private static final String PROTOCOL = "telnet";

	private static final int DEFAULT_PORT = 23;

	private TelnetProtocolHandler handler;
	private Socket socket;

	private InputStream is;
	private OutputStream os;
	private int width = 10;
	private int height = 10;
	private int localWidth = 320;
	private int localHeight = 200;

	private boolean connected = false;
	

	public TELNET() {

	}
	
	public TELNET(final String host, final int port) throws UnknownHostException {

		InetAddress ia = null;
		
		try {
			ia = InetAddress.getByName(host);
			connected = true;
		} catch (UnknownHostException ex) {
			connected = false;
			try {
				showMessage("Error: Host incorrect.");
				ERROR("Error: while connecting to server:", ex.toString());
			} catch (InterruptedException e1) {
				ERROR("Exception:", e1.toString());
				e1.printStackTrace();
			}

		}
		try{
			socket = new Socket(ia, port);
		} catch (IOException e) {
			Log.d(TAG, "IO Exception connecting to host", e);
		}	
		
		handler = new TelnetProtocolHandler() {
			/** get the current terminal type */
			@Override
			protected String getTerminalType() {
				// TODO Auto-generated method stub
				return null;
			}

			/** get the current window size */
			@Override
			public int[] getWindowSize() {
				return new int[] { width, height };
			}

			/** notify about local echo */
			@Override
			public void setLocalEcho(boolean echo) {
				/* EMPTY */
			}

			/** write data to our back end */
			@Override
			public void write(byte[] b) throws IOException {
				if (os != null)
					os.write(b);
			}

			/** sent on IAC EOR (prompt terminator for remote access systems). */
			@Override
			public void notifyEndOfRecord() {
			}

						

			@Override
			protected String getCharsetName() {
//				Charset charset = getCharset();
//				if (charset != null)
//					return charset.name();
//				else
					return "";
			}
		};
	}

	
	 
	public boolean isConnected() {
		return connected;
	}

	@Override
	public int read(byte[] buffer, int start, int len) {
		/* process all already read bytes */
		int n = 0;
		try {			

			do {
				n = handler.negotiate(buffer, start);
				if (n > 0)
					return n;
			} while (n == 0);

			while (n <= 0) {
				do {
					n = handler.negotiate(buffer, start);
					if (n > 0)
						return n;
				} while (n == 0);
				n = is.read(buffer, start, len);
				if (n < 0) {
					//bridge.dispatchDisconnect(false);
					throw new IOException("Remote end closed connection.");
				}

				handler.inputfeed(buffer, start, n);
				n = handler.negotiate(buffer, start);
			}
		} catch (IOException e) {
			ERROR("Exception:", e.toString());
			e.printStackTrace();

			try {
				showMessage("Error while reading stream.");
				ERROR("Error while reading stream.");
			} catch (InterruptedException e1) {
				ERROR("Exception:", e.toString());
				e1.printStackTrace();
			}

			this.disconnect();
		}

		return n;
	}
	
	

	@Override
	public void write(byte[] buffer) {
		
		DEBUG("writing data:", buffer);

		try {
			if (os != null)
				os.write(buffer);
			os.flush();
		} catch (IOException e) {
			ERROR("Exception:", e.toString());
			e.printStackTrace();

			try {
				showMessage("Error while writing to stream.");
				ERROR("Error while writing to stream.");
			} catch (InterruptedException e1) {
				ERROR("Exception:", e1.toString());
				e1.printStackTrace();
			}

			disconnect();
		}
	}
	
	@Override
	public void write(int c) {
		try {
			if (os != null)
				os.write(c);
			os.flush();
		} catch (IOException e) {
			ERROR("Exception:", e.toString());
			e.printStackTrace();

			try {
				showMessage("Error while writing to stream.");
			} catch (InterruptedException e1) {
				ERROR("Exception:", e1.toString());
				e1.printStackTrace();
			}
		}
	}

	
	
	@Override
	public void connect() {
		
		Thread conn = new Thread(new ConnectionRunnable());
		
		conn.setName("Connection");
		conn.setDaemon(true);
		conn.start();
	}

	private class ConnectionRunnable implements Runnable {
		public void run() {
			INFO("Connecting to server.");
			
			try {
				

				connected = true;

				is = socket.getInputStream();
				os = socket.getOutputStream();

			}catch (UnknownHostException e) {
				ERROR("Exception:", e.toString());
				e.printStackTrace();
				try {
					showMessage("Error while connecting to server.");
					ERROR("Error while connecting to server:", e.toString());
				} catch (InterruptedException e1) {
					ERROR("Exception:", e1.toString());
					e1.printStackTrace();
				}
				
				disconnect();
			} catch (IOException e) {
				ERROR("Exception:", e.toString());
				e.printStackTrace();
				try {
					showMessage("Error while getting streams.");
					ERROR("Error while getting streams.");
				} catch (InterruptedException e1) {
					ERROR("Exception:", e1.toString());
					e1.printStackTrace();
				}

				disconnect();
			} finally {
				
				DEBUG("Connected to server");
				
				connected = true;
				
				startRelay();
			}
		}
	}

	@Override
	public void resize(int width, int height, int localWidth, int localHeight) {

		this.width = width;
		this.height = height;

		this.localWidth = localWidth;
		this.localHeight = localHeight;
//		
//		if (channel == null) return;
//		
//		synchronized (channel) {
//			channel.setPtySize(width, height, localWidth, localHeight);
//		}
	}

	@Override
	public void disconnect() {

		connected = false;
		stopRelay();
		if (socket != null)
		try {
				socket.close();
				socket = null;
		} catch (IOException e) {
				Log.d(TAG, "Error closing telnet socket.", e);
		}
		System.exit(0);
	}
}
